<?php


namespace Simanx\Spes\Attribute;

use ReflectionAttribute;
use ReflectionClass;
use ReflectionMethod;

/**
 * 用于管理一个类Attribute
 * @package Simanx\Spes\Attribute
 */
class ClassAttributeProxy
{
    public array $attributes = [];

    public function __construct(private ReflectionClass $class)
    {
    }

    public function getClass(): ReflectionClass
    {
        return $this->class;
    }

    /**
     * @param string|object|ReflectionClass $target
     * @return static
     * @throws \ReflectionException
     */
    public static function create($target)
    {
        if (is_string($target)) {
            if (!class_exists($target)) {
                throw new \InvalidArgumentException('class ' . $target . ' not found');
            }

            return new static(new ReflectionClass($target));
        } elseif (is_object($target)) {
            if ($target instanceof ReflectionClass) {
                return new static($target);
            }

            return new static(new ReflectionClass($target));
        }

        return new static($target);
    }

    public function getClassName()
    {
        return $this->class->name;
    }

    /**
     * 获取class上的多个attribute实例
     * @param string|null $attrName
     * @param int $flags
     * @return array|object[]
     */
    public function getAttributes(
        string $attrName = null,
        int $flags = ReflectionAttribute::IS_INSTANCEOF)
    {
        return array_map(function (ReflectionAttribute $attribute) {
            return $attribute->newInstance();
        }, $this->class->getAttributes($attrName, $flags));
    }

    /**
     * 获取class上的一个attribute实例
     * @param string|null $attrName
     * @param int $flags
     * @return object|null
     */
    public function getAttribute(
        string $attrName = null,
        int $flags = ReflectionAttribute::IS_INSTANCEOF): object|null
    {
        $attributes = $this->getAttributes($attrName, $flags);
        if (!$attributes) {
            return null;
        }

        return $attributes[0];
    }

    /**
     * 获取所有方法的Attribute实例
     * @param string|null $attrName
     * @param int $attrFlags
     * @param int $methodFlags
     * @return array [ <method> => [attribute, ...], ... ]
     */
    public function getMethodAttributes(
        string $attrName = null,
        int $attrFlags = ReflectionAttribute::IS_INSTANCEOF,
        int $methodFlags = ReflectionMethod::IS_PUBLIC)
    {
        $attributes = [];
        foreach ($this->class->getMethods($methodFlags) as $method) {
            $methodAttributes = [];
            foreach ($method->getAttributes($attrName, $attrFlags) as $attribute) {
                $methodAttributes[] = $attribute->newInstance();
            }

            $attributes[$method->getName()] = $methodAttributes;
        }

        return $attributes;
    }

    /**
     * 从对应方法获取多个Attribute实例
     * @param string $methodName
     * @param string|null $attrName
     * @param int $attrFlags
     * @return array
     * @throws \ReflectionException
     */
    public function getAttributesFromMethod(
        string $methodName,
        string $attrName = null,
        int $attrFlags = ReflectionAttribute::IS_INSTANCEOF): array
    {
        $method = $this->class->getMethod($methodName);
        if (!$method) {
            throw new \InvalidArgumentException("method: $methodName has not exists");
        }

        $attributes = $method->getAttributes($attrName, $attrFlags);
        return array_map(function (ReflectionAttribute $attribute) {
            return $attribute->newInstance();
        }, $attributes);
    }

    /**
     * 从对应方法获取一个对应Attribute实例
     * @param string $methodName
     * @param string $attrName
     * @param int $attrFlags
     * @return mixed|null
     * @throws \ReflectionException
     */
    public function getAttributeFromMethod(
        string $methodName,
        string $attrName,
        int $attrFlags = ReflectionAttribute::IS_INSTANCEOF)
    {
        $attributes = $this->getAttributesFromMethod($methodName, $attrName, $attrFlags);
        if (!$attributes) {
            return null;
        }

        return $attributes[0];
    }

    public function getPropertiesAttributes(
        string $attrName = null,
        int $attrFlags = ReflectionAttribute::IS_INSTANCEOF,
        int $propFlags = \ReflectionProperty::IS_PUBLIC
    ): array
    {
        $properties = $this->class->getProperties($propFlags);
        $result = [];
        foreach ($properties as $property) {
            foreach ($property->getAttributes($attrName, $attrFlags) as $attr) {
                $result[$property->getName()] = $attr->newInstance();
            }
        }

        return $result;
    }

    /**
     * 从对应属性获取多个Attribute实例
     * @param string $propertyName
     * @param string $attrName
     * @param int $flags
     * @return array|object[]
     * @throws \ReflectionException
     */
    public function getAttributesFromProperty(
        string $propertyName,
        string $attrName,
        int $flags = ReflectionAttribute::IS_INSTANCEOF)
    {
        $property = $this->class->getProperty($propertyName);
        return array_map(function (ReflectionAttribute $attribute) {
            return $attribute->newInstance();
        }, $property->getAttributes($attrName, $flags));
    }

    /**
     * 从属性中获取一个Attribute实例
     * @param string $propertyName
     * @param string $attrName
     * @param int $flags
     * @return object|null
     * @throws \ReflectionException
     */
    public function getAttributeFromProperty(
        string $propertyName,
        string $attrName,
        int $flags = ReflectionAttribute::IS_INSTANCEOF): object|null
    {
        $attributes = $this->getAttributesFromProperty($propertyName, $attrName, $flags);
        if (!$attributes) {
            return null;
        }

        return $attributes[0];
    }
}